package com.zhuqieli.database_learn.utils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.zhuqieli.database_learn.config.OSSConfig;
import com.zhuqieli.database_learn.pojo.FileTable;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.groovy.tools.shell.IO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author xucf-c
 * @data 2023/8/31 15:41
 */

@Component
public class UploadUtils {
    @Autowired
    RedisUtils redisUtils;

    @Autowired
    OSSConfig ossConfig;
    private String bucketName = "xucfglodon";

    // 本地临时存放切分文件的文件夹路径
    private String localChunkFolder = "D://";
    public List<FileTable> getFileInfosAndUploadToOSS(MultipartFile[] myfiles, Integer userid, String username) throws IOException {
        List<FileTable> filesTables = new ArrayList<>();
        Queue<String> queue = new LinkedList<>();
        for (MultipartFile myfile : myfiles) {
            // 获得文件名
            String fileName = myfile.getOriginalFilename();
            // 文件名判空
            if (StringUtils.isBlank(fileName)) {
                continue;
            }
            // 文件名去空格
            fileName = fileName.replace(" ", "");
            // 检查文件名是否合法
            if (checkUploadFileName(fileName)) {
                String[] fileAndSuffix = StringUtils.split(fileName, ".");
                String thumbnailUrl = null;
                if ("jpg".equals(fileAndSuffix[1])) {
                    thumbnailUrl = ThumbnailUtils.generateAndSaveThumbnail(myfile.getBytes(), 100, 100, username, fileName);
                }
                long fileSize = myfile.getSize();
                String fileType = myfile.getContentType();
                FileTable fileTable = assembleFileTable(userid, fileName, fileSize, fileType, username, thumbnailUrl);
                redisUtils.setFileCache(fileName, myfile.getBytes());
                queue.offer(fileName);
                filesTables.add(fileTable);
            }
        }
        if (filesTables.size() > 0) {
            OSS ossClient = ossConfig.ossClient();
            for (FileTable fileTable : filesTables) {
                String fileCacheKey = queue.poll();
                if (redisUtils.cacheKeyExists(fileCacheKey)) {
                    // key存在 完整性校验
                    byte[] fileCache = redisUtils.getFileCache(fileCacheKey);
                    if (fileCache.length != fileTable.getFileSize()) {
                        fileCache = redisUtils.getFileCache(fileCacheKey);
                    }
                    // 如果文件大小超过100M进行大文件切分上传
                    if (fileTable.getFileSize() > 1024 * 1024 * 100) {
                        uploadFileChunks(fileCache, username, fileTable.getFileName(),ossClient);
                    } else {
                        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileTable.getUrl() + "/" + fileTable.getFileName() +"."+ fileTable.getSuffix(), new ByteArrayInputStream(fileCache));
                        // 执行文件上传
                        ObjectMetadata metadata = new ObjectMetadata();
                        metadata.setContentDisposition("attachment;filename=" + URLEncoder.encode(fileTable.getUrl(), StandardCharsets.UTF_8.name()));
                        metadata.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
                        metadata.setContentLength(fileCache.length);
                        putObjectRequest.setMetadata(metadata);
                        // 上传文件
                        ossClient.putObject(putObjectRequest);
                        // 上传缩略图文件
                        if (fileTable.getThumbnailUrl() != null) {
                            ObjectMetadata metadata1 = new ObjectMetadata();
                            metadata1.setContentType(MediaType.TEXT_HTML_VALUE);
                            metadata1.setContentLength(fileCache.length);
                            // 设置为inline类型可以对图片进行预览
                            metadata1.setContentDisposition("inline");
                            PutObjectRequest thumPutObjectRequest = new PutObjectRequest(bucketName, "thumbnail/" + fileTable.getUrl() + "/" + fileTable.getFileName() +"."+ fileTable.getSuffix(), new java.io.File(fileTable.getThumbnailUrl()),metadata1);
                            ossClient.putObject(thumPutObjectRequest);
                        }
                        redisUtils.removeFileCache(fileCacheKey);
                    }
                }
            }
            ossClient.shutdown();
        }
        return filesTables;
    }
    // 组装fileTable对象
    private static FileTable assembleFileTable(Integer userid, String fileName, Long fileSize, String fileType, String username, String thumbnailUrl) {
        FileTable fileTable = new FileTable();
        // 生成雪花id
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
        long id = idGenerator.nextId();
        String[] fileAndSuffix = StringUtils.split(fileName, ".");
        fileTable.setSnowflakeId(id);
        fileTable.setUserId(userid);
        fileTable.setUrl(username);
        fileTable.setThumbnailUrl(thumbnailUrl);
        fileTable.setIsDeleted(0);
        fileTable.setPublicStatu(0);
        fileTable.setFileSize(fileSize);
        fileTable.setFileName(fileAndSuffix[0]);
        fileTable.setSuffix(fileAndSuffix[1]);
        fileTable.setCreatedAt(new Date());
        return fileTable;
    }

    //检查文件名是否合法
    public Boolean checkUploadFileName(String fileName) {
        final String format = "[^\\u4E00-\\u9FA5\\uF900-\\uFA2D\\w-_.,()（）《》]";
        Pattern pattern = Pattern.compile(format);
        Matcher matcher = pattern.matcher(fileName);
        return !matcher.find() && fileName.contains(".");
    }

    // 切片上传大文件
    public void uploadFileChunks(byte[] fileBytes, String username, String filename, OSS ossClient) throws IOException {
        String filenameWithoutExtension = filename.substring(0, filename.lastIndexOf('.'));
        // 获取文件后缀
        String fileExtension = filename.substring(filename.lastIndexOf('.'));
        // 切分大小定义为1MB
        int chunkSize = 1024 * 1024;
        int numChunks = (int) Math.ceil((double) fileBytes.length / chunkSize);
        // 远程上传路径
        String completeObjectName = username + "/" + filename;
        List<PartETag> partETags = new ArrayList<>();
        //对文件分块
        for (int chunkNumber = 0; chunkNumber < numChunks; chunkNumber++) {
            int startIdx = chunkNumber * chunkSize;
            int endIdx = Math.min(startIdx + chunkSize, fileBytes.length);
            byte[] chunk = java.util.Arrays.copyOfRange(fileBytes, startIdx, endIdx);
            String chunkObjectName = username + "/" + filenameWithoutExtension + "_chunk_" + chunkNumber + fileExtension;
            String localChunkFilePath = localChunkFolder + "/" + chunkObjectName;
            FileOutputStream localChunkOutputStream = new FileOutputStream(localChunkFilePath);
            localChunkOutputStream.write(chunk);
            localChunkOutputStream.close();
            UploadPartRequest uploadRequest = new UploadPartRequest();
            uploadRequest.setBucketName(bucketName);
            uploadRequest.setKey(chunkObjectName);
            uploadRequest.setInputStream(new FileInputStream(localChunkFilePath));
            uploadRequest.setPartSize(chunk.length);
            uploadRequest.setPartNumber(chunkNumber + 1);
            UploadPartResult uploadPartResult = ossClient.uploadPart(uploadRequest);
            partETags.add(new PartETag(chunkNumber + 1, uploadPartResult.getETag()));
            // 删除本地分块文件
            File localChunkFile = new File(localChunkFilePath);
            localChunkFile.delete();
        }
        // 完成分块上传
        InitiateMultipartUploadRequest initiateRequest = new InitiateMultipartUploadRequest(bucketName, completeObjectName);
        InitiateMultipartUploadResult initiateResult = ossClient.initiateMultipartUpload(initiateRequest);
        String uploadId = initiateResult.getUploadId();
        CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(bucketName, completeObjectName, uploadId, partETags);
        CompleteMultipartUploadResult completeResult = ossClient.completeMultipartUpload(completeRequest);
        ossClient.shutdown();
    }
}
